/********************************************************************************
* @file    uart_ble_module.c （从机端）
* @author  jianqiang.xue
* @version V1.0.0
* @date    2022-04-02
* @brief   [协议]
串口格式: 0x55 0x56 CMD(1byte) LEN(1byte) DATA(*) SUM(1byte)
NUS格式:  CMD(1byte) LEN(1byte) DATA(*) SUM(1byte)

CMD： 高7bit决定数据通道方向。 0--串口 1--NUS
LEN:  DATA长度
SUM： 将CMD+LEN+DATA进行累计，然后取低8位。
********************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <string.h>
#include <stdbool.h>

#include "bsp_gpio.h"
#include "bsp_uart.h"
#include "log.h"
/* Private Includes ----------------------------------------------------------*/
#include "ls_gpio.h"
#include "ls_syscfg.h"
#include "app_main.h"
#include "uart_ble_module.h"
#if LS_POWER_SWITCH
#include "biz_power.h"
#endif
#if LS_NRF_BLE_SUPPORT
#include "biz_ble.h"
#endif
/* Private Define ------------------------------------------------------------*/

#define PROT_CMD_HEAD_PACK(BUFF, CMD, LEN)     \
do                                             \
{                                              \
    BUFF[0] = 0x55;                            \
    BUFF[1] = 0x56;                            \
    BUFF[2] = CMD;                             \
    BUFF[3] = LEN;                             \
}while(0);                                     \

/* External Variables --------------------------------------------------------*/
/* Private Macro -------------------------------------------------------------*/
/* Private Variables ---------------------------------------------------------*/
uint8_t data_temp[32]               = {0};

/* Private Function Prototypes -----------------------------------------------*/
static uint8_t get_checksum(uint8_t *data, uint8_t len)
{
    uint16_t sum = 0;
    for (uint8_t i = 0; i < len; i++)
    {
        sum += data[i];
    }
    return sum & 0x00FF;
}

/**
 * @brief  发送数据给ble串口通道(消息入队，非实时发送)
 * @param  *data: 欲发送内容
 * @param  len: 内容长度(字节大小)
 */
static void send_data(uint8_t *data, uint16_t len)
{
    if ((data[2] & 0x80) == 0x80)
    {
        biz_ble_data_send(data + 2, len - 2);
    }
    else
    {
        bsp_uart_send_nbyte(BOARD_UART_COMM, data, len);
    }
}

/**
 * @brief  [MCU->BLE] 获取设备信息 -- 响应指令
 */
static void response_get_device_info(bool dir)
{
    uint8_t temp_len = 0;
    PROT_CMD_HEAD_PACK(data_temp, (dir << 8) | PROT_CMD_GET_DEVICE_INFO_RES, BLE_MAC_MAX_VAL + strlen(FIRMWARE_DATE));
    temp_len = 4;
    // mac
    memcpy(&data_temp[4], get_ble_mac_addr(), BLE_MAC_MAX_VAL);
    temp_len += BLE_MAC_MAX_VAL;
    // version
    memcpy(&data_temp[temp_len], FIRMWARE_DATE, strlen(FIRMWARE_DATE));
    temp_len += 6;
    // CHECKSUM
    data_temp[temp_len] = get_checksum(data_temp + 2, temp_len - 2);
    send_data(data_temp, temp_len + 1);
}

/**
 * @brief  [MCU->BLE] 获取设备名称 -- 响应指令
 */
static void response_get_device_name(bool dir)
{
    uint8_t temp_len = 0;
    PROT_CMD_HEAD_PACK(data_temp, (dir << 8) | PROT_CMD_GET_DEVICE_NAME_RES, strlen(PRODUCT_NAME));
    temp_len = 4;
    // 设备名称
    memcpy(data_temp + temp_len, PRODUCT_NAME, strlen(PRODUCT_NAME));
    temp_len += strlen(PRODUCT_NAME);

    // CHECKSUM
    *(data_temp + temp_len) = get_checksum(data_temp + 2, temp_len - 2);
    send_data(data_temp, temp_len + 1);
}

/**
 * @brief  [BLE->MCU] 获取电量状态 -- 响应指令
 * @note   协议包(括号里面是字节位)：HEADER(0,1) CMD(2) LENGTH(3) BAT_SOC(4) CHANRG(5) CHECKSUM(6)
 *         BAT_LEVEL: 0~100(success)||0xff(0xff)
 */
static void response_bat_state(bool dir)
{
#if LS_POWER_SWITCH
    uint8_t temp_len = 0;
    PROT_CMD_HEAD_PACK(data_temp, (dir << 8) | PROT_CMD_GET_BAT_SOC_RES, 2);
    temp_len = 4;
    // bat_soc
    data_temp[4] = get_bat_soc_val();
    data_temp[5] = get_usb_state();
    temp_len += 2;
    // CHECKSUM
    *(data_temp + temp_len) = get_checksum(data_temp + 2, temp_len - 2);
    send_data(data_temp, temp_len + 1);
#endif
}

/**
 * @brief  [BLE->MCU] 获取蓝牙地址 -- 响应指令
 * @note   协议包(括号里面是字节位)：HEADER(0,1) CMD(2) LENGTH(3) MAC(4,5,6,7,8,9) CHECKSUM(10)
 */
static void response_ble_addr(bool dir)
{
    uint8_t temp_len = 0;
    PROT_CMD_HEAD_PACK(data_temp, (dir << 7) | PROT_CMD_GET_BLE_ADDR_RES, BLE_MAC_MAX_VAL);
    temp_len = 4;
    // mac
    memcpy(&data_temp[4], get_ble_mac_addr(), BLE_MAC_MAX_VAL);
    temp_len += BLE_MAC_MAX_VAL;
    // CHECKSUM
    data_temp[temp_len] = get_checksum(data_temp + 2 , temp_len - 2);
    send_data(data_temp, temp_len + 1);
}

/**
 * @brief  [BLE->MCU] 蓝牙进入配对模式(直连广播) -- 响应指令
 * @note   协议包(括号里面是字节位)：HEADER(0,1) CMD(2) LENGTH(3) RESULT(4) CHECKSUM(5)
 *         RESULT 0--success 1--failed
 */
static void response_ble_enter_pair_mode(bool dir)
{
    uint8_t temp_len = 0;
    PROT_CMD_HEAD_PACK(data_temp, (dir << 7) | PROT_CMD_BLE_IN_PAIRING_RES, 1);
    temp_len = 4;
    // 处理蓝牙进入直连广播(暂时未写) biz_ble_services.c
    // 先判断当前是否广播，如果有，则先暂停广播，修改广播信息，再起开启广播。填写返回状态
    data_temp[4] = 0;
    temp_len += 1;
    // CHECKSUM
    *(data_temp + temp_len) = get_checksum(data_temp + 2, temp_len - 2);
    send_data(data_temp, temp_len + 1);
    LOG_I("<INFO> [ble_enter_pair]: %02x\r\n", data_temp[4]);
}

/**
 * @brief  [BLE->MCU] 蓝牙进入白名单配对模式(白名单广播) -- 响应指令
 * @note   协议包(括号里面是字节位)：HEADER(0,1) CMD(2) LENGTH(3) RESULT(4) CHECKSUM(5)
 *         RESULT 0--success 1--failed
 */
static void response_ble_enter_whitelist_pair_mode(bool dir)
{
    uint8_t temp_len = 0;
    PROT_CMD_HEAD_PACK(data_temp, (dir << 7) | PROT_CMD_BLE_WHITELIST_PAIRING_REQ, 1);
    temp_len = 4;
    // 处理蓝牙进入直连广播(暂时未写) biz_ble_services.c
    // 先判断当前是否广播，如果有，则先暂停广播，修改广播信息，再起开启广播。填写返回状态
    data_temp[4] = 0;
    temp_len += 1;
    // CHECKSUM
    *(data_temp + temp_len) = get_checksum(data_temp + 2, temp_len - 2);
    send_data(data_temp, temp_len + 1);
    LOG_I("<INFO> [ble_enter_pair]: %02x\r\n", data_temp[4]);
}

/**
 * @brief  [BLE->MCU] 蓝牙连接状态获取 -- 响应指令
 * @note   协议包(括号里面是字节位)：HEADER(0,1) CMD(2) LENGTH(3) RESULT(4) CHECKSUM(5)
 *         RESULT 0--未连接 1--已连接
 */
static void response_ble_connect_state(bool dir)
{
    uint8_t temp_len = 0;
    PROT_CMD_HEAD_PACK(data_temp, (dir << 7) | PROT_CMD_BLE_CONN_STATE_RES, 1);
    temp_len = 4;
    data_temp[4] = get_ble_conn_state();
    temp_len += 1;
    // CHECKSUM
    *(data_temp + temp_len) = get_checksum(data_temp, temp_len);
    send_data(data_temp, temp_len + 1);
    LOG_I("<INFO> [ble_conn_ste]: %02x\r\n", data_temp[4]);
}

/**
 * @brief  [MCU->MP] 命令透传指令 -- MCU -> MP(手机)
 * @note   协议包(括号里面是字节位)：HEADER(0,1) CMD(2) LENGTH(3) 协议包(n字节) CHECKSUM(n+1)
 */
static void response_mcu_to_mp(uint8_t *data, uint16_t len)
{
#if BLE_NUS_ENABLED && LS_NRF_BLE_SUPPORT
    biz_ble_data_send(data, len);
#endif
}

/**
 * @brief  [MCU->MP] 命令透传指令 -- MP(手机) -> MCU
 * @note   协议包(括号里面是字节位)：HEADER(0,1) CMD(2) LENGTH(3) 协议包(n字节) CHECKSUM(n+1)
 */
static void response_mp_to_mcu(uint8_t *data, uint16_t len)
{
    bsp_uart_send_nbyte(BOARD_UART_COMM, data, len);
}

/**
 * @brief  [协议第一层] 对CMD进行分别处理
 * @param  *data: 数据指针
 * @param  len: 数据长度
*/
static void prot_dispose_data(uint8_t *data, uint16_t len)
{
    // 将首字节的方向去除
    LOG_D("<DEBUG> [nrf_ble]: %x  %x\r\n", ((uint8_t)(data[0] << 1) >> 1), data[0]);
    switch(((uint8_t)(data[0] << 1 )>> 1))
    {
        case PROT_CMD_GET_DEVICE_INFO_REQ:
            response_get_device_info((data[0] & 0x80));
            LOG_D("<DEBUG> [nrf_ble]: PROT_CMD_GET_DEVICE_INFO_REQ\r\n");
            break;
        case PROT_CMD_GET_DEVICE_NAME_REQ:
            response_get_device_name((data[0] & 0x80));
            LOG_D("<DEBUG> [nrf_ble]: PROT_CMD_GET_DEVICE_NAME_REQ\r\n");
            break;
        case PROT_CMD_GET_BAT_SOC_REQ:
//           LOG_D("<DEBUG> [nrf_ble]: PROT_CMD_GET_BAT_SOC_RES %02x %02x %02x %02x\r\n", data[3], data[4], data[5], data[6]);
            response_bat_state((data[0] & 0x80));
            break;
        case PROT_CMD_GET_BLE_ADDR_REQ:
            response_ble_addr((data[0] & 0x80));
            LOG_D("<DEBUG> [nrf_ble]: PROT_CMD_GET_BLE_ADDR_REQ\r\n");
            break;
        case PROT_CMD_BLE_IN_PAIRING_RES:
            response_ble_enter_pair_mode((data[0] & 0x80));
            LOG_D("<DEBUG> [nrf_ble]: PROT_CMD_BLE_IN_PAIRING_REQ %d\r\n", data[4]);
            break;
        case PROT_CMD_BLE_WHITELIST_PAIRING_REQ:
            response_ble_enter_whitelist_pair_mode((data[0] & 0x80));
            LOG_D("<DEBUG> [nrf_ble]: PROT_CMD_BLE_WHITELIST_PAIRING_REQ %d\r\n", data[4]);
            break;
        case PROT_CMD_BLE_CONN_STATE_REQ:
            response_ble_connect_state((data[0] & 0x80));
            LOG_D("<DEBUG> [nrf_ble]: PROT_CMD_BLE_CONN_STATE_REQ %d\r\n", data[4]);
            break;
        case PROT_CMD_MCU_TO_MP:
            response_mcu_to_mp(data, len);
            LOG_D("<DEBUG> PROT_CMD_MCU_TO_MP \r\n");
            break;
        case PROT_CMD_MP_TO_MCU:
            response_mp_to_mcu(data, len);
            LOG_D("<DEBUG> PROT_CMD_MP_TO_MCU \r\n");
            break;
        default:
            break;
    }
    if ((data[0] & 0x80) != 0x80)
    {
        bsp_uart_reset_rxbuff(BOARD_UART_COMM);
    }
}

/* Public Function Prototypes ------------------------------------------------*/

/**
 * @brief  [协议] 对串口数据进行协议检查 格式：头(2字节 0x55 0x56) 命令(1字节) 长度(n字节) 检验和(1字节)
 * @note   为biz_uart.c文件提供  [给串口中断回调]
 * @param  *data: 串口数据
 * @param  len: 数据长度
 */
void uart_ble_module_prot_dispose_data(uint8_t *data, uint16_t len)
{
    if (data[0] != 0x55)
    {
        bsp_uart_reset_rxbuff(BOARD_UART_COMM);
        return;
    }
    if (len == 2 && data[1] != 0x56)
    {
        bsp_uart_reset_rxbuff(BOARD_UART_COMM);
        return;
    }

    if (len < data[3] + 5)
    {
        return;
    }
    uint8_t checksum = 0;
    checksum = get_checksum(data + 2, len - 3);
    if (checksum != data[len - 1])
    {
        LOG_E("<ERR> [prot_uart]: check fail %02x|%02x\r\n", checksum, data[len - 1]);
        bsp_uart_reset_rxbuff(BOARD_UART_COMM);
        return;
    }
//     LOG_D("<DEBUG> [nrf_ble]: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x||len:%d\r\n",
//             data[0], data[1], data[2], data[3], data[4],
//             data[5], data[6], data[7], data[8], data[9], len);
    prot_dispose_data(data + 2, len - 3);
}

/**
 * @brief  [协议] 对NUS数据进行协议检查 格式：命令(1字节) 长度(n字节) 检验和(1字节)
 * @note   为biz_uart.c文件提供 [给nus接收中断回调]
 * @param  *data: NUS数据
 * @param  len: 数据长度
 */
void nus_ble_module_prot_dispose_data(uint8_t *data, uint16_t len)
{
    if (len < data[1] + 3)
    {
        return;
    }
    uint8_t checksum = 0;
    checksum = get_checksum(data, len - 1);
    if (checksum != data[len - 1])
    {
        LOG_E("<ERR> [prot_nus]: check fail %02x|%02x\r\n", checksum, data[len - 1]);
        bsp_uart_reset_rxbuff(BOARD_UART_COMM);
        return;
    }
//     LOG_D("<DEBUG> [nus_ble]: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x||len:%d\r\n",
//             data[0], data[1], data[2], data[3], data[4],
//             data[5], data[6], data[7], data[8], data[9], len);
    prot_dispose_data(data, len - 1);
}
